package com.heo.controller;

import java.util.Date;
import java.util.HashMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.log4j.Logger;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.heo.pojo.CommandResult;
import com.heo.pojo.ControlInfoEntity;
import com.heo.pojo.DeviceChannelEntity;
import com.heo.pojo.DeviceInfoEntity;
import com.heo.pojo.DeviceLockInfoEntity;
import com.heo.pojo.req.LockDevicePara;
import com.heo.pub.DictEnum;
import com.heo.service.DeviceChannelService;
import com.heo.service.DeviceInfoService;
import com.heo.service.OrgInfoService;
import com.heo.util.RedisUtil;

/**
 * @author chichi
 */

@Controller
@Scope("singleton") // 1、为了性能。2、不需要多例。只要controller中不定义属性，那么单例完全是安全的。
@RequestMapping("/")
public class LockController extends BaseController
{
  private static Logger log = Logger.getLogger(LockController.class);

  @Resource(name = "orgInfoService")
  private OrgInfoService orgInfoService;

  @Resource(name = "deviceInfoService")
  private DeviceInfoService deviceInfoService;

  @Resource(name = "deviceChannelService")
  private DeviceChannelService deviceChannelService;

  /**
   * 启动设备 不同的指令，不同的action入口
   * 
   * @param session
   * @param request
   * @param startDeviceEntity
   * @return
   */
  @ResponseBody
  @RequestMapping(value = "/lock.do", method = { RequestMethod.POST })
  public JSONObject startDevice(HttpSession session, HttpServletRequest request, @RequestBody LockDevicePara lockDevicePara)
  {
    try
    {
      log.debug("[锁定设备]收到请求参数：" + lockDevicePara.toString());
      String ipAddress = getIpAddr(request);
      // 校验参数
      if (StringUtils.isEmpty(lockDevicePara.getDeviceIMEI()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.PARA_ERROR, CommandResult.PublicResultMsg.PARA_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 找设备的信息
      DeviceInfoEntity deviceInfo = deviceInfoService.getByKey(lockDevicePara.getDeviceIMEI());
      if (deviceInfo == null || deviceInfo.getOrgInfoEntity() == null)
      {
        // 设备不存在。device_info是子表，并且有做关联，所以不可能出现子表有数据，父表没数据的情况。所以如果找不到数据，一定是子表没数据。
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_IS_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断appid是否合法
      if (!(deviceInfo.getOrgInfoEntity().getAppId().equals(lockDevicePara.getAppId()) && deviceInfo.getOrgInfoEntity().getAppSecret().equals(lockDevicePara.getAppSecret())))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.AUTHENTICATION_ERROR, CommandResult.PublicResultMsg.AUTHENTICATION_ERROR);
        return JSON.parseObject(commandResult.toString());
      }
      // 检查tocken是否合法
      if (StringUtils.isEmpty(lockDevicePara.getLockTocken()))
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.LOCK_TOCKEN_IS_NULL, CommandResult.PublicResultMsg.LOCK_TOCKEN_IS_NULL);
        return JSON.parseObject(commandResult.toString());
      }
      // 判断设备的通道参数是否合法
      if (StringUtils.isEmpty(lockDevicePara.getDeviceChannelIndex()))
      {
        // 通道参数不能为空
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      else
      {
        if (lockDevicePara.getDeviceChannelIndex() + 1 > deviceInfo.getDeviceChannelNum())
        {
          CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
          return JSON.parseObject(commandResult.toString());
        }
      }
      // 判断通道是否支持锁定
      HashMap<String, Object> paraMap = new HashMap<String, Object>();
      paraMap.put("deviceImei", lockDevicePara.getDeviceIMEI());
      paraMap.put("deviceChannelId", lockDevicePara.getDeviceChannelIndex());
      DeviceChannelEntity deviceChannelEntity = deviceChannelService.getByMap(paraMap);
      if (deviceChannelEntity == null)
      {
        // 没有找到对应的通道参数
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
        return JSON.parseObject(commandResult.toString());
      }
      if (deviceChannelEntity.getDeviceLockMode() == 0)
      {
        // 没有找到对应的通道参数
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_LOCK_IS_UNSUPPORTED, CommandResult.PublicResultMsg.DEVICE_CHANNEL_LOCK_IS_UNSUPPORTED);
        return JSON.parseObject(commandResult.toString());
      }

      // 去redis里找设备的控制信息
      Object redisObj = RedisUtil.getValue(DictEnum.RedisKey.DEVICE_CONTROL + lockDevicePara.getAppId() + ":" + lockDevicePara.getDeviceIMEI());
      if (redisObj == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_IS_OFFLINE, CommandResult.PublicResultMsg.DEVICE_IS_OFFLINE);
        return JSON.parseObject(commandResult.toString());
      }
      ControlInfoEntity controlInfo = (ControlInfoEntity) JSON.parseObject(redisObj.toString(), ControlInfoEntity.class);
      // 如果设备在redis里的信息不为null，那么就需要查询这个设备的通道的当前的状态
      Byte currStatus = controlInfo.getChannelStatus(lockDevicePara.getDeviceChannelIndex());
      if (currStatus == null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_INDEX_NOT_EXIST, CommandResult.PublicResultMsg.DEVICE_CHANNEL_INDEX_NOT_EXIST);
        log.error("[锁定设备]锁定设备错误，当前设备在redis里的信息不存在。可能已经离线，也可能设备的通道号不存在。");
        return JSON.parseObject(commandResult.toString());
      }
      
      // 去redis里查找设备是否存在有预约锁定的状态
      // key格式：[LOCK_ORDER_STATUS][DEVICE_IEMI]12345678[DEVICE_CHANNEL_INDEX]0  
      String redisLockOrderKey = DictEnum.RedisKey.LOCK_ORDER_STATUS + lockDevicePara.getAppId() + ":" + lockDevicePara.getDeviceIMEI() + ":" + lockDevicePara.getDeviceChannelIndex();
      redisObj = RedisUtil.getValue(redisLockOrderKey);
      if(currStatus == DictEnum.DeviceChannelStatus.WORKING) {
    	//设备没有被提前预约，且在工作中，可提前预约
          if (redisObj == null)
          {
        	  // 保存预约锁定信息到redis里
              RedisUtil.addValue(redisLockOrderKey, lockDevicePara.toString());
          }else {
        	  // 设备当前处于被提前预约状态，不允许二次预约
              CommandResult commandResult = new CommandResult(CommandResult.QueryDeviceResultCode.LOCKED, CommandResult.QueryDeviceResultMsg.LOCKED);
              log.error("[锁定设备]设备当前处于被提前预约状态，不允许二次预约。");
              return JSON.parseObject(commandResult.toString());
          }
      }
      
      // 去redis里查找设备是否处于已经锁定的状态
      // key格式：[LOCK_STATUS][DEVICE_IEMI]12345678[DEVICE_CHANNEL_INDEX]0 
      String redisLockKey = DictEnum.RedisKey.LOCK_STATUS + lockDevicePara.getAppId() + ":" + lockDevicePara.getDeviceIMEI() + ":" + lockDevicePara.getDeviceChannelIndex();
      redisObj = RedisUtil.getValue(redisLockKey);
      // 如果redisObj==null，表示设备没有被锁定。     
      if (redisObj != null)
      {
        CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.DEVICE_CHANNEL_IS_LOCKING, CommandResult.PublicResultMsg.DEVICE_CHANNEL_IS_LOCKING);
        return JSON.parseObject(commandResult.toString());
      }

      // 如果支持锁定的话，就看下最大的锁定时间。如果送进来的参数比锁定时间大，就重置为锁定时间
      int lockSecond = 60 * 10;// 需要锁定的时间秒数，默认是10分钟锁定
      if (StringUtils.isEmpty(lockDevicePara.getLockTime()) || lockDevicePara.getLockTime() > deviceChannelEntity.getDeviceLockTime() || lockDevicePara.getLockTime() < 0)
      {
        lockSecond = deviceChannelEntity.getDeviceLockTime();
      }
      else
      {
        lockSecond = lockDevicePara.getLockTime();
      }

      // 现在开始锁定设备
      DeviceLockInfoEntity deviceLockInfoEntity = new DeviceLockInfoEntity();
      deviceLockInfoEntity.setLockEndTime(new Date().getTime() + lockSecond * 1000);//锁定结束的时间
      deviceLockInfoEntity.setLockTocken(lockDevicePara.getLockTocken());

      // 保存锁定信息到redis里
      RedisUtil.addValue(redisLockKey, deviceLockInfoEntity.toString(), lockSecond);//锁定的时间，单位秒
      
      // 返回锁定成功
      CommandResult commandResult = new CommandResult(CommandResult.PublicResultCode.SUCCESS, CommandResult.PublicResultMsg.SUCCESS);
      log.error("[锁定设备]设备锁定成功。redisLockKey:" + redisLockKey);
      return JSON.parseObject(commandResult.toString());
    }
    catch (Exception e)
    {
      log.error(e.getMessage(), e);
      // 未知错误
      CommandResult pushResult = new CommandResult(CommandResult.PublicResultCode.UNKNOW_ERROR, CommandResult.PublicResultMsg.UNKNOW_ERROR);
      return JSON.parseObject(pushResult.toString());
    }
  }
}
